package viz_ui;

import java.awt.Color;

import controllers.ControlManager;
import processing.core.PApplet;
import processing.core.PFont;
import buttons.Button;
import utilities.TreeMapBuilder;
import view.V_DisplayObject;
import viz_main.ScatterPlotCAxis;
import viz_main.VizApplet;
import viz_main.TreeComponent;
import viz_main.Node;
import java.util.ArrayList;

import model.CategoryManager;

public class TreeMapView extends V_DisplayObject{
	private VizApplet parent;
	private Button[] internalDisplayList;
	
	private ScatterPlotCAxis axis;
	private ScatterPlotCAxis axis2;
	private ScatterPlotCAxis axis3;
	
    private final float PAD = 1; // a pad value (adjust to increase/decrease tightness to the sides
    private final float X_INDENT = 0; // amount of extra indent we add to the x axis
    private final float Y_INDENT = 70; // amount of extra indent we add to the y axis
    
	private float scale = 1.0f;
	private PFont labelFont; // used for text labels
	private PFont largeFont;
	private PFont numFont; // used for numbers
	private int minFntSz = 8;
	private int maxFntSz = 12;
	private int curFntSz = 18;
	
    private float yStart;
    private float yEnd ; 
    private float xStart ;
    private float xEnd ;
    private int max;
    private Node tree;
    private String selectedStreet = " ";
    private String selectedBlock = " ";
    private String selectedSide = " ";
	
	private ArrayList<TreeComponent> treeComps = new ArrayList<TreeComponent>();
	private ArrayList<TreeComponent> treeComps_Level2 = new ArrayList<TreeComponent>();
	
	public TreeMapView(float xPos, float yPos, float w, float h, ControlManager c, VizApplet p)
	  {
		this.init(xPos, yPos, w, h, new Color(230, 230, 230, 150));
	    parent = p;
	    // NOTE: this component manages the display of its internal buttons but it sends the update
	    // management for interaction to the controlManager.
	    
	    curFntSz = (minFntSz+maxFntSz)/2;
	    largeFont = p.createFont("Arial-Black-12", 16);
	    labelFont = p.createFont("Arial-Black-12", curFntSz);
	    numFont = p.createFont("ArialMT-12", minFntSz);

	    //internalDisplayList = new Button[]{xLabelList};
	    
	    yStart = yLoc + ySize - curFntSz - PAD - Y_INDENT;
	    yEnd = yLoc + PAD + 4; 
	    xStart = xLoc + PAD + X_INDENT + curFntSz;
	    xEnd = xLoc+xSize-8;
	  }
	  
	  public void makeTree(ScatterPlotCAxis street, ScatterPlotCAxis block, ScatterPlotCAxis side)
	  {
	    axis = street;
	    axis2 = block;
	    axis3 = side;
	    // Create a root node:
		tree = new Node("Streets", street.getUniqueCategories().size());
		// add each type of street to the tree as its own unique node:
	    for(int i = 0; i < street.getUniqueCategories().size(); i++)
	    {
	    	String currentCategory = axis.getUniqueCategories().get(i);
	    	Node newChild = new Node(currentCategory, axis.getCategoryMap().get(currentCategory), axis.getLabel(), tree);
		    tree.addChild(newChild); 
		    // create the data structure recursively...
		    generateTree(new ScatterPlotCAxis[] {block, side}, newChild);
	    }
	    // display the contents of the tree:
	    //System.out.println();
//	    System.out.println("===========");
	    //System.out.println(tree.getRecursiveTotal(0) + " nodes (not including root node)");
	    //System.out.println(street.getUniqueCategories().size() + " streets");
//	    tree.printTree("");
//	    System.out.println("===========");
	     
	    
	    int width = (int) (xEnd-xStart);
	    int height = (int) (yStart-yEnd);
	    float boxTop = yEnd;
	    float boxLeft = xStart;
	    float total = tree.getTotal();
	    int number = axis.getUniqueCategories().size();
		
		treeComps = new ArrayList<TreeComponent>();
		// Pre-generate all the treeComponents with zero values
		for (int i = 0; i < tree.getCount(); i++){
			String name = tree.getChildren().get(i).getName();
			TreeComponent c = new TreeComponent(0,0,0,0,parent.boxColor,tree.getChildren().get(i));
			c.setName(name);
			c.setStrokeSize(1.2f);
			treeComps.add(c);
		}
		// Use a treeBuilder to generate the values actual values for each component
		TreeMapBuilder builderLevel1 = new TreeMapBuilder(width, height, 0, 0, treeComps);
		builderLevel1.CalcMap();
		
		// offset all values so that they are positioned at the correct location:
		for (int i = 0; i < treeComps.size(); i++){
			treeComps.get(i).offset(xStart, yEnd);
		}

		System.out.print("adding internal nodes...");
		treeComps_Level2 = new ArrayList<TreeComponent>();
		// now for each treeComponent, iterate through each of it's arrays of children
		// assign those to a new group of tree components and pass it down
		for (int i = 0; i < treeComps.size(); i++)
		{
			// create a new array of internal treeComponents based on the current treeComponents internal children
			ArrayList<TreeComponent> tmpComponents = new ArrayList<TreeComponent>();
			Node curNode = treeComps.get(i).getNode();
			for (int j = 0; j < curNode.getChildren().size(); j++){
				String name = curNode.getChildren().get(j).getName();
				TreeComponent cc = new TreeComponent(0,0,0,0, parent.boxColor, curNode.getChildren().get(j));
				cc.setName(name);
				cc.setIsInner(true);
				tmpComponents.add(cc);
			}
			// pass the parameters of this treeComponents as the size and position to be used when building 
			// its internal nodes:
			TreeMapBuilder builderLevel2 = new TreeMapBuilder(
					treeComps.get(i).getXSize(), 
					treeComps.get(i).getYSize(), 
					0, 
					0, 
					tmpComponents
			);
			builderLevel2.CalcMap();
			
			// offset the positions to the correct location because they are created at 0,0 by default.
			for (int e = 0; e < tmpComponents.size(); e++){
				tmpComponents.get(e).offset(treeComps.get(i).getXLoc(), treeComps.get(i).getYLoc());
			}
			
			// add all the temp components we created onto the level 2 components array:
			treeComps_Level2.addAll(tmpComponents);
		}
		System.out.println("done.");
	  } // END OF makeTree() method
	  
	  // recursively generate a tree data structure based on the given list of axis.
	  // dynamically adjust the top level axis and the array to dig further down into the heirarchy
	  private void generateTree(ScatterPlotCAxis[] axisGroup, Node top)
	  {
		  if(axisGroup.length > 0)
		  {
			    ScatterPlotCAxis currentAxis = axisGroup[0];
			    
			    // Search through and create a new set of unique categories based on the element indexes
			    // from the newChild node. (while referring to our next axis)
			    // this is because there may be many instances of block 5120 but only 5 pertain to "Georgia St."
			    // we only want the instances of block 5120 that pertain to our current street
			    CategoryManager innerCategories = new CategoryManager();
			    for(int j = 0; j < top.getCount(); j++)
			    {
			    	if(currentAxis.getElementAsString(j) != null) {
			    		innerCategories.addElement(currentAxis.getElementAsString(j), top.getIndexList()[j]);
			    	}
			    }
			    
			    // Make a new array with the element at index 0 spliced out
			    // this allows us to progress through the hierarchy
			    ScatterPlotCAxis[] tmpArray = new ScatterPlotCAxis[axisGroup.length-1];
			    for(int i = 0; i < tmpArray.length; i++)
				{
			    	tmpArray[i] = axisGroup[i+1];
				}
			    
			    // create a new node based on a separate list of more "unique-er" categories:
			    for(int k = 0; k < innerCategories.getUniqueCategories().size(); k++)
			    {
			    	String currentCategory = innerCategories.getUniqueCategories().get(k);
			    	Node innerChild = new Node(currentCategory, innerCategories.getCategoryMap().get(currentCategory), currentAxis.getLabel(), top);
			    	top.addChild(innerChild);
			    	generateTree(tmpArray, innerChild);
			    }
		  }
	  }

	  // Req. non-null axis and an already existing array of bars:
	  public void updateTree()
	  {
	    if(treeComps != null){
	      for(int i = 0; i < treeComps.size(); i++)
	      {
	        //barComps[i].setXLoc(xAxis.getElement(i));
	        //barComps[i].setYLoc(yAxis.getElement(i));
	      }
	    }
	  }
	  
	  public int calcPercent(){
		  int amount = 0;
		  for (int i = 0; i < tree.getChildrenLength();i++){
			  amount += tree.getNode(i).getCount();
		  }
		  return amount;
	  }
	  
	  @Override
	  public void display(PApplet p) {
	    p.noStroke();
	    p.fill(bgColor);
	    p.rect(xLoc, yLoc, xSize, ySize-10);
	    p.fill(0);
    
    /* *
     * DRAW lines and numbers:
     * */
	    p.textFont(largeFont);
	    p.stroke(100);
	    p.strokeWeight(1);
	    p.fill(255);
	    
	    float width = xEnd-xStart;
	    float height = yStart-yEnd;
	    float boxTop = yEnd;
	    float boxLeft = xStart;

	    // draw the bounding rectangle:
	    p.fill(200,200,200);
	    p.rect(xStart,yEnd,width,height);
	    updateComponents(p);
	    
	    p.fill(0);
  	  	p.text("Ticket Location: ", xStart, ySize -8);
  	    p.textFont(labelFont);
  	  	p.text("Street (Left click) : "+ selectedStreet, xStart, ySize+5);
  	  	p.text("Block (Right Click) : "+ selectedBlock, xStart, ySize +15);
  	    //p.text("Side of Street: "+ selectedSide, xStart, ySize +25);

	  }
	  

	  public boolean drawVertically(float w, float h){
		  return w > h;
	  }
	  
	  public float getXStart() {
		  return xStart;
	  }
	  public float getXEnd() {
		  return xEnd;
	  }	  
	  
	  
	  public float getYStart() {
		  return yStart;
	  }
	  public float getYEnd() {
		  return yEnd;
	  }	  
	  
	  public ArrayList<TreeComponent> getAllBoxes()
	  {
		  return treeComps;
	  }
	  
	  public ArrayList<TreeComponent> getInnerBoxes()
	  {
		  return treeComps_Level2;
	  }
	  
	  public Node getTree()
	  {
		  return tree;
	  }
	  
	  public void updateComponents(PApplet p)
	  {
		// draw the inner components:
	    for(int i = 0; i < treeComps_Level2.size(); i++)
	    {
	    	treeComps_Level2.get(i).display(parent);
	    }  
		  
	    // draw all the topLevel components:
	    for(int i = 0; i < treeComps.size(); i++)
	    {
	    	treeComps.get(i).display(parent);
	    }
	   
	  }
	  public void setdisplayInfo(String street, String block, String side)
	  {
		  selectedStreet = street;
		  selectedBlock = block;
		  selectedSide = side;
	  }
}
